/*
    Contact info:
    bhepple@freeshell.org
    http://bhepple.freeshell.org/scrmgr

    Copyright (C) 1991-2008 Bob Hepple

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

*/
/* SCRMGR front end to grep(1) */

#include <stdio.h>
#include <getopt.h>
#include <setjmp.h>
#include <sys/types.h>		/* for stat */
#include <sys/stat.h>		/* for stat */
#include <unistd.h>		/* for stat */
#include "smlib.h"
#include "smio.h"
#include "backup.h"
#include "smgrep.def"

CHAR *smgrep_rcsid(void) {
    return("$Id: smgrep.c,v 1.6 2008/02/19 13:15:50 bhepple Exp $");
}

static struct {
    CHAR	*rev_string;
} global;

SM_FILE	screenset;

jmp_buf		NoMoreHeap;

static CHAR *usage_msg[] = {
"Provide an easy to use front end to grep(1) for finding text.",
"",
NULL
};

#if !READ_DAT_AT_RUNTIME
static CHAR *smgrep_dat[] = {
#include "smgrep.dat"
NULL,
};
#endif

static void usage()
{
    CHAR **c;
    
    fprintf(stderr, "%s of %s.\n", global.rev_string, sm_program_name);
    fprintf(stderr, "Usage: %s\n", sm_program_name);
    
    c = usage_msg;
    while (*c) {
        fputs(*c, stderr);
        fputc('\n', stderr);
        c++;
    }
    exit(2);
}

void sm_error_exit ARG1(CHAR *, msg)
{
    sm_term();
    puts(msg);
    exit(1);
}

void re_fail ARG2(char *, msg, char, op)
{
    sm_message(msg);
}

/* Exported to the SCRMGR package */
f_ptr sm_get_field_trigger ARG1(SM_FIELD, field)
{
    return(NULL);
}

CALLBACK(do_hit)
{
    int		retval = command;
    CHAR	*buf, *line = alloca(SM_MAXREC), *p, *linenum, *filename;

    switch (command) {
    case VIEW_FILE:
    case EDIT_FILE:
	if (command == VIEW_FILE) {
	    p = getenv("PAGER");
	    if (p && *p)
		strcpy(line, p);
	    else
		strcpy(line, "more");
	} else {
	    p = getenv("EDITOR");
	    if (p && *p)
		strcpy(line, p);
	    else
		strcpy(line, "vi");
	}
	strcat(line, " +");
	buf = smdb_get_current(hit_scr);
	filename = buf;
	if (buf && (p = strchr(buf, grep_scr->parent->separator)) != 0) {
	    *p++ = 0;
	    linenum = p;
	    if ((p = strchr(linenum, grep_scr->parent->separator)) != 0) {
		*p = 0;
		strcat(line, linenum);
		strcat(line, " ");
		strcat(line, filename);
		*p = grep_scr->parent->separator;
		smio_system(line, 0);
	    }
	    *(linenum-1) = grep_scr->parent->separator;
	}
	sm_setup_edit(SM_BASE_WINDOW,
		      hit_scr,
		      SM_QUIT_C,
		      NULL,
		      hit_skey,
		      NULL);
	retval = 0;
	break;
    default:
	break;
    }
    return(retval);
}

static int build_grep_line ARG2(CHAR *, buf, SM_SCREEN, screen)
{
    CHAR	c;

    if (!*sm_fld_get(screen, "files")) {
	sm_message("Which files to search?");
	return(1);
    }

    if (!*sm_fld_get(screen, "pattern")) {
	sm_message("What pattern to look for?");
	return(1);
    }

    strcpy(buf, "grep -n ");
    buf = strchr(buf, 0);

    c = *sm_fld_get(screen, "reverse");
    if (c == 'Y') {
	strcat(buf, "-v ");
	buf = strchr(buf, 0);
    }
    c = *sm_fld_get(screen, "ignore_case");
    if (c == 'Y') {
	strcat(buf, "-i ");
	buf = strchr(buf, 0);
    }

    strcat(buf, "'");
    strcat(buf, sm_fld_get(screen, "pattern"));
    strcat(buf, "' ");
    buf = strchr(buf, 0);

    strcat(buf, sm_fld_get(screen, "files"));
    strcat(buf, " /dev/null");
    buf = strchr(buf, 0);
    return(0);
}

static int do_it ARG1(SM_SCREEN, screen)
{
    CHAR	*command = alloca(SM_MAXREC);
    CHAR	*buf = command;
    CHAR	tmp[100];
    int		retval = 0;

    if (build_grep_line(buf, screen))
	return(retval);
    buf = strchr(buf, 0);

    sm_mktemp(tmp, "TXXXXXX");
    sprintf(buf, ">%s", tmp);

    if (smio_system(command, 1) != 0) 
	sm_message("Nothing found!");
    else {
	smdb_delete_data(hit_scr);
	
	/* read in results */
	smdb_read_file(tmp, hit_scr, 1);

	/* parse out the filename and linenumbers */
	while ((buf = smdb_get_current(hit_scr)) != NULL) {
	    CHAR	*p;

	    if ((p = strchr(buf, ':')) != NULL)
		*p = hit_scr->parent->separator;
	    if ((p = strchr(buf, ':')) != NULL)
		*p = hit_scr->parent->separator;
	    smdb_set_current(hit_scr, buf);
	    smdb_read_next(hit_scr);
	}
	smdb_seek(hit_scr, (INDEX) 0);

	sm_setup_scr(SM_BASE_WINDOW, 
		     grep_scr, 
		     NULL,
		     NULL,
		     0,
		     0,
		     0);
	sm_add_callback(do_hit, NULL);
	sm_setup_edit(SM_BASE_WINDOW,
		      hit_scr,
		      SM_QUIT_C,
		      NULL,
		      hit_skey,
		      NULL);
	retval = 1;
    }

    /* Clean up */
    unlink(tmp);
    return(retval);
}

static int save_command ARG2(CHAR *, filename, SM_SCREEN, screen)
{
    CHAR	*buf = alloca(SM_MAXREC);
    FILE	*file;
    int		retval = 0;

    if (backup_file(filename) < 0) {
	sprintf(buf, 
		"Can't make a backup file for \"%s\"", 
		filename);
	sm_message(buf);
    } 
    
    if ((file = fopen(filename, "w")) == NULL) {
	sm_file_error("Can't write to file: ", filename, errno);
	if (file)
	    fclose(file);
	retval = 1;
    } else {
        CHAR	*buf = alloca(SM_MAXREC);

	build_grep_line(buf, screen);
	fputs(buf, file);
	fputc('\n', file);
	fclose(file);
	sprintf(buf, "Command line saved in \"%s\".", filename);
	sm_message(buf);
    }
    return(retval);
}

CALLBACK(do_save_command)
{
    FILE	*file;
    CHAR	*filename;

    /* Always entered after sm_prompt */
    if (command == SM_YES_C) {
	filename = sm_get_prompt_value();

	if ((file = fopen(filename, "r")) != NULL) {
	    CHAR	*buf = alloca(100);

	    fclose(file);
	    sprintf(buf, "\"%s\" already exists. Overwrite it?", filename);
	    if (sm_confirm(buf) != SM_YES_C)
		return(SM_QUIT_C);
	}
	save_command(filename, grep_scr);
	command = SM_POPCALLBACK;
    }

    return(command);
}

static void setup_save_command()
{
    CHAR	pwd[MAXPATHLEN + 1];

    GETCWD(pwd, MAXPATHLEN);
    sm_add_callback(do_save_command, NULL);
    sm_setup_prompt("Enter file for the command line (current directory is:)", 
		    pwd, 
		    NULL,
		    NULL);
}

static void display_command ARG1(SM_SCREEN, screen)
{
    CHAR	*buf = alloca(SM_MAXREC);

    if (build_grep_line(buf, screen) == 0)
	sm_message(buf);
}

CALLBACK(smgrep_main_loop)
{
    int		reset_change;
    
    switch(command) {
	
    case DO_IT:
	if (sm_scr_changed(grep_scr)) {
	    sm_set_scr_unchanged(grep_scr);
	    if (do_it(grep_scr))
		return(0);
	} else {
	    sm_setup_scr(SM_BASE_WINDOW, 
			 grep_scr, 
			 NULL,
			 NULL,
			 0,
			 0,
			 0);
	    sm_add_callback(do_hit, NULL);
	    sm_setup_edit(SM_BASE_WINDOW,
			  hit_scr,
			  SM_QUIT_C,
			  NULL,
			  hit_skey,
			  NULL);
	    return(0);
	}
	break;

    case SAVE_COMMAND:
	setup_save_command();
	return(0);

    case DISPLAY_COMMAND:
	display_command(grep_scr);
	break;

    case SM_EXIT_C:
    case SM_NO_SAVE_EXIT_C:
	return(command);
	break;

    default: /* Probably a return from one of the sub routines */
	break;
    }

    if (sm_scr_changed(grep_scr))
	reset_change = 1;
    else
	reset_change = 0;
    sm_setup_scr(SM_BASE_WINDOW, 
		 grep_scr, 
		 NULL,
		 NULL,
		 0,
		 0,
		 0);
    if (reset_change)
	sm_set_scr_changed(grep_scr);
    return(0);
}

int main ARG2(int, argc, CHAR **, argv)
{
    int			c;

    /* Temporary back door */
    if (setjmp(NoMoreHeap)) {
	alloca(0);
	sm_error_exit(sm_mem_error);
    }

    global.rev_string = "$Revision: 1.6 $";

    sm_pre_init(argc, argv);
    /* Get rid of X options and enter X if appropriate */
    sm_init(1, argc, argv);

    while ((c = getopt(argc, argv, "?")) != EOF)
        switch (c) {
        default:  
	    /* Unfortunately, if we've entered X in sm_init, then any error 
	       messages disappear */
            sm_term();
	    usage(); 
	    break;
        }
    
#ifdef SM_DEBUG
    smio_flushinp();
#endif

    screenset = sm_scrmgr_screens();

#if READ_DAT_AT_RUNTIME
    {
	CHAR	buf[MAXPATHLEN + 1];

	buf[0] = 0;
#if NEEDS_PATH_FOR_PROGS
	strcpy(buf, sm_program_directory);
#endif
	strcat(buf, "smgrep.dat");
	if (sm_fopen(screenset, buf) == SM_ERROR)
	    sm_error_exit("Error reading smgrep.dat!");
    }
#else
    if (sm_dbopen(screenset, smgrep_dat) == SM_ERROR)
	sm_error_exit("Error reading internal data block!");
#endif

#include "smgrep.opn"

    /* Back door for when we run out of heap - horrible structure, but
       what can one do ?*/

    if (setjmp(NoMoreHeap)) {
	alloca(0);

	/* Prevent infinite loop */
	if (setjmp(NoMoreHeap)) {
	    alloca(0);
	    sm_error_exit(sm_mem_error);
	}

	sm_message(sm_mem_error);

	if (setjmp(NoMoreHeap)) {
	    /* Oh boy - really serious! */
	    alloca(0);
	    sm_error_exit("Ran out of heap - file(s) were not saved! Sorry!");
	}

	alloca(0);

	if (setjmp(NoMoreHeap) == 0)
	    sm_term();
	exit(1);
    }

    /*************/
    /* Main loop */
    /*************/

    sm_message(global.rev_string);

    sm_add_callback(smgrep_main_loop, NULL);	
    sm_setup_scr(SM_BASE_WINDOW, 
		 grep_scr, 
		 NULL,
		 NULL,
		 0,
		 0,
		 0);
    sm_set_scr_changed(grep_scr);
    sm_main_loop();
    sm_term();
    exit(0);
}

/* For emacs:
 * Local Variables:
 * tab-width: 8
 * End:
 */
